/*  tBookShelf: TextPlugin
    (C) 2007  TvN (Volodymyr Tarasenko), e-mail: tvntsr@yahoo.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef FONT_MANAGER_PLUGINS_HPP
#define FONT_MANAGER_PLUGINS_HPP

#include <string>
#include <functional>
#include <map>
#include <vector>

#include "Utils/TextStyle.hpp"
#include "Utils/SharedPtr.hpp"

extern "C"
{
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/ftglyph.h>
}

#include "Utils/Area.hpp"

class FontManagerError : public std::exception
{
#define  FONTMANAGERERROR_BUF 256
    std::string msg;
public:
    FontManagerError(int error, const char* where)
        : exception()
    {
        char buf[FONTMANAGERERROR_BUF];
        snprintf(buf, 
                 FONTMANAGERERROR_BUF, 
                 "Font Manager (%s) Error, code %d", 
                 where,
                 error);
        msg = buf;
    };

    FontManagerError(const std::string& error)
        : exception(), msg(error)
    {    };

    virtual ~FontManagerError() throw()
    {}

    virtual const char* what() const throw()
    {
        return msg.c_str();
    }

#undef FONTMANAGERERROR_BUF

};


struct cmpCompare: public std::binary_function<std::string, std::string, bool>
{
    bool
    operator()(const std::string& a, const std::string& b) const
    {
        return strcasecmp(a.c_str(), b.c_str()) < 0;
    }
};

class GlyphTable
{
public:
    std::pair<FT_UInt, FT_Glyph>        GLYPH_PAIR;
    typedef std::map<FT_UInt, FT_Glyph> GLYPH_TABLE;

public:
    GlyphTable()
        : mTable()
    {}

    ~GlyphTable()
    {
        for(GLYPH_TABLE::iterator p = mTable.begin();
            p != mTable.end();
            ++p)
        {
            FT_Done_Glyph(p->second);
        }
    }

    void insert(FT_UInt key, FT_Glyph data)
    {
        GLYPH_TABLE::value_type item(key, data);

        mTable.insert(item);
    }

    bool find(const FT_UInt key, FT_Glyph& to)
    {
        GLYPH_TABLE::iterator p = mTable.find(key);
        if (p == mTable.end())
        {
            return false;
        }

        to = p->second;

        return true;
    }
private:
    GLYPH_TABLE mTable;
};

class FontManager
{
private:
    typedef std::pair<Point, FT_Glyph> GLYPHY;
    typedef std::vector<GLYPHY>        GLYPHY_LINE;
    typedef SharedPtr<GlyphTable>      SHARED_GLYPHS;

    struct FM_Face
    {
        SHARED_GLYPHS mGlyphs;
        FT_Face       mFace;

        FM_Face(GlyphTable* new_glyphs, FT_Face face)
            : mGlyphs(new_glyphs),
              mFace(face)
        {}
    };

    // styles, face
    typedef std::map<std::string, FM_Face, cmpCompare>     FONT_STYLES;
    // font name, styles
    typedef std::map<std::string, FONT_STYLES, cmpCompare> FONTS;
public:
    FontManager();
    ~FontManager();

    void addFont(const std::string& file);

    // Checks is font already loaded, if not and download false returns false,
    // if download true - download font if it was not loadded before
    bool isFontAvailable(const std::string& file, 
                         bool               download = false);

    void useFont(const std::string&      file, 
                 const bool              add = true);

    /// style: "Regular", "Bold", "Italic", "Bold Italic"
    void useFont(const std::string&      font_name, 
                 const std::string&      style);

    void useKerning(const bool           use = true)
    {
        mUseKerning = use;
    }

    void setHyphenationManager(HyphenationManager *hyph)
    {
        mHyphenationManager = hyph;
    }

    // Change font too
    void setStyle(const TextStyle&       style);

    // Current font is used
    void setStyle(const size_t           font_size,
                  const Align            align);

    // Return rendered number of chars. It could be less than str len 
    // if passed string too long for given output
    int  renderLine(DrawingArea&         area,
                    const char*          str);

    void renderChar(DrawingArea&         area,
                    const char&          str);

    /// Return line witdh in pixels
    int getLineWidth(const char*         str);

//     int fitString(const char*            wstr, 
//                   size_t                 max_width,
//                   int&                   end);

    int renderMultiLines(DrawingArea&       area,
                         const std::string& str,
                         const TextStyle&   style,
                         bool               imitate);
protected:
    void addCreatedFace(FT_Face         face);

    bool drawImage(DrawingArea&         area, 
                   FT_Glyph             glyph, 
                   const int            start);

    FT_BBox fillGlyphs(FT_Bool          kerning,
                       const char*      str, 
                       size_t&          num_chars, 
                       GLYPHY_LINE&     glyph_line,
                       const size_t     max_width = 32768);

    int getAlignLen(const DrawingArea&  area,
                    const FT_BBox&      bbox);

    FT_Glyph getGlyph(const char symbol);
private:
    FT_Library mLibrary;
    FONTS      mFonts;

    FM_Face    mCurrentFace;
    bool       mDeleteCurrent;
    bool       mUseKerning;

    FT_Matrix  mRotateMatrix;
   
    size_t          mFontSize;
    Align           mAlign;

    HyphenationManager *mHyphenationManager;
};


#endif //FONT_MANAGER_PLUGINS_HPP
